package com.exp.security.config.sms;

import com.exp.security.config.DBUserDetailsManager;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;

/**
 * @author zhangxj
 * @date 2025/1/9
 * @Description 短信验证码登录的认证提供者
 */
public class SmsAuthenticationProvider implements AuthenticationProvider {
    /**
     * 短信验证码前缀- redis key前缀
     */
    private static final String SMS_PREFIX = "sms:phone:";

    private final DBUserDetailsManager dbUserDetailsManager;

    private final StringRedisTemplate stringRedisTemplate;

    public SmsAuthenticationProvider(DBUserDetailsManager dbUserDetailsManager, StringRedisTemplate stringRedisTemplate) {
        this.dbUserDetailsManager = dbUserDetailsManager;
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;
        // 获取主体信息，也就是用户输入的手机号码
        Object principal = authentication.getPrincipal();
        String phone = "";
        if (principal instanceof String) {
            phone = (String) principal;
        }
        if (StringUtils.isBlank(phone)) {
            throw new InternalAuthenticationServiceException("请输入手机号码");
        }
        UserDetails userDetails = dbUserDetailsManager.loadUserByPhone(phone);
        if (userDetails == null) {
            throw new InternalAuthenticationServiceException("手机号码未注册");
        }

        // 获取凭证，用户输入的验证码
        String inputCode = (String) authenticationToken.getCredentials();
        String cacheCode = stringRedisTemplate.opsForValue().get(SMS_PREFIX + phone);
        if (StringUtils.isBlank(cacheCode)) {
            throw new BadCredentialsException("验证码已过期，请重新获取验证码");
        }
        if (!cacheCode.equalsIgnoreCase(inputCode)) {
            throw new BadCredentialsException("验证码不正确");
        }


        // 创建已认证对象
        SmsAuthenticationToken authenticationResult = SmsAuthenticationToken.authenticated(userDetails, inputCode, userDetails.getAuthorities());
        authenticationResult.setDetails(authenticationToken.getDetails());
        return authenticationResult;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return SmsAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
